#include"Shader.h"
#include"Debug.h"

#include<iostream>

namespace Craft{


Shader::Shader()
{
}

Shader& Shader::use()
{
	glUseProgram(this->m_ID);
	return *this;
}

void Shader::compile(const GLchar* vertexSource, const GLchar* fragmentSource, const GLchar* geometrySource)
{
	GLuint sVertex, sFragment, gShader;

	bool has_geomertySource = true;
	if (geometrySource == nullptr)
	{
		has_geomertySource = false;
	}

	// init vertex shader
	sVertex = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(sVertex, 1, &vertexSource, NULL);
	glCompileShader(sVertex);
	check_compile_errors(sVertex, "VERTEX");

	// init fragment shader
	sFragment = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(sFragment, 1, &fragmentSource, NULL);
	glCompileShader(sFragment);
	check_compile_errors(sVertex, "FRAGMENT");

	// if exist geometry source 
	if (has_geomertySource)
	{
		gShader = glCreateShader(GL_GEOMETRY_SHADER);
		glShaderSource(gShader, 1, &geometrySource, NULL);
		glCompileShader(gShader);
		check_compile_errors(gShader, "GEOMETRY");
	}

	m_ID = glCreateProgram();
	glAttachShader(m_ID, sVertex);
	glAttachShader(m_ID, sFragment);

	if (has_geomertySource)
	{
		glAttachShader(m_ID, gShader);
	}

	glLinkProgram(m_ID);
	check_compile_errors(m_ID, "PROGRAM");

	// clear
	glDeleteShader(sVertex);
	glDeleteShader(sFragment);
	if (has_geomertySource)
	{
		glDeleteShader(gShader);
	}
}


void Shader::check_compile_errors(GLuint object, std::string type)
{
	GLint success;
	GLchar infoLog[1024];
	if (type != "PROGRAM")
	{
		glGetShaderiv(object, GL_COMPILE_STATUS, &success);
		if (!success)
		{
			glGetShaderInfoLog(object, 1024, NULL, infoLog);
			std::cout << " ERROR::SHADER: Compile-time error: Type: " << type << infoLog << std::endl;
		}
	}
	else
	{
		glGetProgramiv(object, GL_LINK_STATUS, &success);
		if (!success)
		{
			glGetProgramInfoLog(object, 1024, NULL, infoLog);
			std::cout << " ERROR::Shader: Link-time error: Type: " << type << infoLog << std::endl;
		}
	}
}


void Shader::SetVector3f(const GLchar *name, const glm::vec3 &value, GLboolean useShader)
{
	if (useShader)
	{
		use();
	}

	glUniform3f(glGetUniformLocation(m_ID, name),value.x,value.y,value.z);
}


void Shader::SetVector4f(const GLchar *name, const glm::vec4 &value, GLboolean useShader)
{
	if (useShader)
	{
		use();
	}

	glUniform4f(glGetUniformLocation(m_ID, name), value.x, value.y, value.z,value.w);
}


void Shader::SetMatrix4(const GLchar *name, const glm::mat4 &matrix, GLboolean useShader )
{
	if (useShader)
	{
		use();
	}

	glUniformMatrix4fv(glGetUniformLocation(m_ID, name), 1, GL_FALSE, glm::value_ptr(matrix));
}

void Shader::SetInteger(const GLchar *name, const int& value, GLboolean useShader )
{
	if (useShader)
	{
		use();
	}

	glUniform1i(glGetUniformLocation(m_ID, name), value);
}


}